// 版权所有2011 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// CPU配置文件。
// 
// 分析时钟勾号的信号处理程序将新堆栈跟踪
// 添加到最近跟踪的日志中。该日志由用户goroutine读取，
// 将其转换为格式化的配置文件数据。如果读卡器没有保持
// 与日志的同步，这些写入操作将被记录为丢失记录的计数。
// 实际配置文件缓冲区位于proffuf.go中。

package runtime

import (
	"runtime/internal/atomic"
	"runtime/internal/sys"
	"unsafe"
)

const maxCPUProfStack = 64

type cpuProfile struct {
	lock mutex
	on   bool     // 评测在
	log  *profBuf // 此处写入的评测事件

	// extra持有addNonGo 
	// 中累积的额外堆栈，对应于到达
	// 非Go创建线程的评测信号。这些堆栈被写入
	// 以记录下一次正常Go线程获取
	// 信号处理程序时的日志。
	// 假设堆栈每个有2个字（我们没有从这些线程得到
	// 一个完整的回溯），加上一个字
	// 帧大小，100 Hz分析将每秒生成
	// 300字。
	// 希望正常的Go线程至少每隔几秒钟就会收到一次分析
	// 信号。
	extra      [1000]uintptr
	numExtra   int
	lostExtra  uint64 // 由于extra已满而丢失的帧数
	lostAtomic uint64 // 由于处于mips/arm上的atomic64而丢失的帧数；更新的racily 
}

var cpuprof cpuProfile

// SetCPupFileRate将CPU分析速率设置为每秒hz采样数。
// 如果hz<=0，SetCPUProfileRate将关闭评测。
// 如果探查器处于打开状态，则在未先将其关闭的情况下无法更改速率。
// 
// 大多数客户端应该使用runtime/pprof包或
// 测试包的-test.cpuprofile标志，而不是直接调用
// SetCPUProfileRate。
func SetCPUProfileRate(hz int) {
	// 将hz钳制为合理的频率。
	if hz < 0 {
		hz = 0
	}
	if hz > 1000000 {
		hz = 1000000
	}

	lock(&cpuprof.lock)
	if hz > 0 {
		if cpuprof.on || cpuprof.log != nil {
			print("runtime: cannot set cpu profile rate until previous profile has finished.\n")
			unlock(&cpuprof.lock)
			return
		}

		cpuprof.on = true
		cpuprof.log = newProfBuf(1, 1<<17, 1<<14)
		hdr := [1]uint64{uint64(hz)}
		cpuprof.log.write(nil, nanotime(), hdr[:], nil)
		setcpuprofilerate(int32(hz))
	} else if cpuprof.on {
		setcpuprofilerate(0)
		cpuprof.on = false
		cpuprof.addExtra()
		cpuprof.log.close()
	}
	unlock(&cpuprof.lock)
}

// add将堆栈跟踪添加到配置文件中。
// 它是从信号处理程序和其他有限环境中调用的
// 不能分配内存或获取信号时可能持有的锁
// 也不能使用大量
// 堆栈。
// go:nowritebarrierrec 
func (p *cpuProfile) add(gp *g, stk []uintptr) {
	// 简单的cas锁与SetCpupFileRate协调。
	for !atomic.Cas(&prof.signalLock, 0, 1) {
		osyield()
	}

	if prof.hz != 0 { // 暗示cpuprof.log！=nil 
		if p.numExtra > 0 || p.lostExtra > 0 || p.lostAtomic > 0 {
			p.addExtra()
		}
		hdr := [1]uint64{1}
		// 注意：write“知道”参数是&gp.labels，
		// 因为否则其写屏障行为可能不正确。参见
		// 更改此处的参数之前的长注释。
		// 
		// 注意：这可能发生在Windows上，我们在Windows上调用
		// p.add时使用的gp不是当前g，该gp为零，
		// 意味着我们中断了一个没有g的系统线程。
		// 在这种情况下，避免出现故障。
		var tagPtr *unsafe.Pointer
		if gp != nil {
			tagPtr = &gp.labels
		}
		cpuprof.log.write(tagPtr, nanotime(), hdr[:], stk)
	}

	atomic.Store(&prof.signalLock, 0)
}

// addNonGo将非Go堆栈跟踪添加到配置文件中。
// 它是从非Go线程调用的，所以我们根本不能使用太多堆栈，
// 也不能做任何需要g或m的事情。
// 特别是，我们不能调用cpuprof.log.write。
// 相反，我们将堆栈复制到cpuprof.extra，
// 中，下次Go线程
// 获取信号处理事件时，该堆栈将被耗尽。
// go:nosplit 
// go:nowritebarrierrec 
func (p *cpuProfile) addNonGo(stk []uintptr) {
	// 简单的cas锁与SetCPUProfileRate协调。
	// （其他对add或addNonGo的调用应该被阻止
	// ，因为
	// 进程一次只能处理一个SIGPROF。如果不能，这个锁也会序列化它们。）
	for !atomic.Cas(&prof.signalLock, 0, 1) {
		osyield()
	}

	if cpuprof.numExtra+1+len(stk) < len(cpuprof.extra) {
		i := cpuprof.numExtra
		cpuprof.extra[i] = uintptr(1 + len(stk))
		copy(cpuprof.extra[i+1:], stk)
		cpuprof.numExtra += 1 + len(stk)
	} else {
		cpuprof.lostExtra++
	}

	atomic.Store(&prof.signalLock, 0)
}

// addExtra添加了addNonGo排队的“额外”评测事件，到配置文件日志。
// 从Go线程上的信号处理程序
// 或从普通goroutine调用addExtra；无论哪种方式，它都可以使用堆栈
// 并且有一个g。然而，世界可能会停止。
func (p *cpuProfile) addExtra() {
	// 复制累积的非Go配置文件事件。
	hdr := [1]uint64{1}
	for i := 0; i < p.numExtra; {
		p.log.write(nil, 0, hdr[:], p.extra[i+1:i+int(p.extra[i])])
		i += int(p.extra[i])
	}
	p.numExtra = 0

	// 报告任何丢失的事件。
	if p.lostExtra > 0 {
		hdr := [1]uint64{p.lostExtra}
		lostStk := [2]uintptr{
			funcPC(_LostExternalCode) + sys.PCQuantum,
			funcPC(_ExternalCode) + sys.PCQuantum,
		}
		p.log.write(nil, 0, hdr[:], lostStk[:])
		p.lostExtra = 0
	}

	if p.lostAtomic > 0 {
		hdr := [1]uint64{p.lostAtomic}
		lostStk := [2]uintptr{
			funcPC(_LostSIGPROFDuringAtomic64) + sys.PCQuantum,
			funcPC(_System) + sys.PCQuantum,
		}
		p.log.write(nil, 0, hdr[:], lostStk[:])
		p.lostAtomic = 0
	}

}

// CPG文件恐慌。
// 它以前提供对运行时生成的pprof格式概要文件的块的原始访问。
// 生成该格式的详细信息已更改，因此此功能已被删除。
// 
// 已弃用：使用runtime/pprof包，
// 或net/http/pprof包中的处理程序，
// 或改为测试包的-test.cpuprofile标志。
func CPUProfile() []byte {
	panic("CPUProfile no longer available")
}

// go:linkname runtime\u pprof\u runtime\u cyclesPerSecond runtime/pprof.runtime\u cyclesPerSecond 
func runtime_pprof_runtime_cyclesPerSecond() int64 {
	return tickspersecond()
}

// readProfile，提供给runtime/pprof，返回下一块
// 二进制CPU评测堆栈跟踪数据，直到数据可用为止。
// 如果关闭了评测，并且返回了在启用
// 时累积的所有评测数据，则readProfile返回eof=true。
// 调用方必须保存返回的数据和标记，然后才能再次调用readProfile。
// 
// go:linkname runtime\u pprof\u readProfile runtime/pprof.readProfile 
func runtime_pprof_readProfile() ([]uint64, []unsafe.Pointer, bool) {
	lock(&cpuprof.lock)
	log := cpuprof.log
	unlock(&cpuprof.lock)
	data, tags, eof := log.read(profBufBlocking)
	if len(data) == 0 && eof {
		lock(&cpuprof.lock)
		cpuprof.log = nil
		unlock(&cpuprof.lock)
	}
	return data, tags, eof
}
